package modelo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import com.sun.xml.internal.bind.v2.runtime.reflect.ListIterator;

import modelo.Player.Colors;

import cards.Biblioteca;
import cards.Card;
import cards.Cripta;
import cards.Features;
import cards.Scriptorium;
import cards.Features.*;

import cards.Monje;

import tablero.Square;
import tablero.Tablero;
//import vista.ScriptoriumModel;
import vista.VistaConsola;

public class Partida {
	private Tablero tablero;
	private List<Player> playerList;
	private List<UpdateListener> listeners;
	private Monje asesino;
	private Set<Monje> cartasMonjeMesa;
	private int turno;
	boolean empezada = false;
	private final int MAX_MOVES = 2;
	private Misas misa;
	Misas currentMisa;
	private Biblioteca biblioteca = new Biblioteca(null);
	private Scriptorium scriptorium= new Scriptorium(null);
	private Cripta cripta= new Cripta(null);

	public List<BibliotecaModel> getBiblioCards()
	{
		return biblioteca.getLibraryCards();
	}
	
	public List<ScriptoriumModel> getScriptoriumCards()	{
		return scriptorium.getScriptoriumCards();
	}
	
	public List<String> getCriptaCards()
	{
		return cripta.getCriptaCards();
	}

	public Partida() {
		playerList = new ArrayList<Player>();
		tablero = new Tablero();
		listeners = new ArrayList<UpdateListener>();
		turno = 0;
		misa = Misas.Maitines;

	}
	

	public int getCardsForMisa() {
		return misa.getCartasPorMisa();
	}

	private void setNextMisa() {
		Misas a[] = Misas.values();
		int n = 0;
		while (a[n] != misa)
			n++;
		misa = a[(n + 1) % a.length];

	}

	public Misas getMisa() {
		return misa;
	}

	public void inicializarPartida() {
		List<Monje> monjeList = new ArrayList<Monje>();
		if (crearMonjes(monjeList)) {
			reparteHojasSospechoso(monjeList);
			reparteCartasMonje(monjeList);
			for (Player j : playerList)
				j.marcarSospechosos();
			moverACasillaInicial();
		}

	}

	public void moverACasillaInicial() {
		for (Player j : playerList){
			if(j.getUbication()!=null)j.getUbication().removePlayer(j);
			j.setUbicacion(getSquare(1));
		}
	}

	public void setEmpezada() {
		empezada = true;
		notificarPartidaEmpezada();
	}

	// TODO: RECORDAR CAMBIAR for(Player j:playerList){ notifyMisa(j);} a modelo
	// de RED
	public void turnos() {
		boolean terminada = false;
		while (!terminada) {
			turnInc();
			for (Player j : playerList)
				notificarTurnoJugador(j); // turno de cada jugador
			for (Player j : playerList)
				j.reset(); // se resetea el booleano de cada jugador para nuevos
							// movimientos
			if (turno % 4 == 0) {
			    moverACasillaInicial();
				// 1)NOTIFICAR A LA VISTA FUNCION CON JUGADOR PARA CAMBIAR SUS
				// CARTAS
				for (Player j : playerList)
					notifyMisa(j);
				this.setNextMisa();
			}
			if (getSquare(18).getPlayerList().size() >= 1)
				terminada = true; // el jugador que llegue a la capilla gana
		}
	}

	private void notifyMisa(Player j) {
		for (UpdateListener listener : listeners) {
			listener.cardsExchangeMode(j);
		}
	}

	public int askQuestion(Player j, int hand, List<Features> questions) {
		return j.answer(hand, questions);
	}

	public boolean askQuestion(Player j1, Player j2, int where, String name) { // j1
																				// pregunta
																				// j2
																				// responde
		if (j2.answer(where, name)) {
			j1.marcarSospechoso(name);
			return true;
		} else
			return false;
	}

	public Set<Square> getReachableSquares(Player j) {
		Set<Square> validas = null; // validas a 1 casilla de distancia
		if (j != null) {
			Square casillaInicial = getPlayerLocation(j);
			Set<Square> validas2 = new TreeSet<Square>(); // validas a 2
															// casillas de
															// distancia
			validas = tablero.squaresConnectedTo(casillaInicial);
			for (Square square : validas) {
				validas2.addAll(tablero.squaresConnectedTo(square));
			}
			validas.addAll(validas2);
			validas.remove(casillaInicial);
		}
		return validas;
	}

	private void notificarMovements(Player j, Set<Square> casillasValidas) {
		for (UpdateListener listener : listeners) {
			listener.movements(j, casillasValidas);
		}
	}

	private boolean crearMonjes(List<Monje> listaCartas) {
		List<java.util.Hashtable> list = LectorFicheros.read();
		if (list != null) {
			for (java.util.Hashtable tabla : list) {
				List<Features> characteriticsList = new ArrayList<Features>();
				characteriticsList.add(Titulo.valueOf((String) tabla
						.get("categoria")));
				characteriticsList.add(Cabeza.valueOf((String) tabla
						.get("cabeza")));
				characteriticsList.add(Orden.valueOf((String) tabla
						.get("orden")));
				characteriticsList.add(Cuerpo.valueOf((String) tabla
						.get("cuerpo")));
				characteriticsList.add(VelloFacial.valueOf((String) tabla
						.get("peloCara")));
				Monje monje = new Monje((String) tabla.get("nombre"),
						characteriticsList, (String) tabla.get("imagen"));
				listaCartas.add(monje);
			}
			return true;
		} else
			return false;
	}

	private void reparteMonks(List<Monje> listaMonjes, int mesa, int player) {
		cartasMonjeMesa = new HashSet<Monje>();
		for (int i = 0; i < mesa; i++)
			cartasMonjeMesa.add((Monje) listaMonjes.remove(0));
		List<Player> players = getPlayerList();
		for (Player j : players) {
			Set<Monje> cartasMonjeJugador = new HashSet<Monje>();
			for (int i = 0; i < player; i++)
				cartasMonjeJugador.add((Monje) listaMonjes.remove(0));
			j.setCartasMonje(cartasMonjeJugador);
		}
	}

	private boolean reparteCartasMonje(List<Monje> listaMonjes) {
		if (listaMonjes.size() == 24) {
			Collections.shuffle(listaMonjes); // Barajar las cartas de Monje
			switch (getPlayerList().size()) {

			case 1:
				reparteMonks(listaMonjes, 13, 10);
				break;

			case 2:
				reparteMonks(listaMonjes, 3, 10);
				break;

			case 3:
				reparteMonks(listaMonjes, 5, 6);
				break;

			case 4:
				reparteMonks(listaMonjes, 3, 5);
				break;

			case 5:
				reparteMonks(listaMonjes, 3, 4);
				break;

			case 6:
				reparteMonks(listaMonjes, 5, 3);
			}// end switch;
			asesino = listaMonjes.remove(0);
			return true;
		} else
			return false;
	}

	public void reparteHojasSospechoso(List<Monje> listaMonjes) {
		Set<Sospechoso> sospechosos = new TreeSet<Sospechoso>();
		for (Monje monje : listaMonjes) {
			sospechosos.add(new Sospechoso(monje));
		}
		for (Sospechoso s : sospechosos) {
			s.setId();
		}
		for (Player j : playerList) {
			j.setHojaDeSospechosos(new TreeSet(sospechosos));
		}
	}

	public void turnInc() {
		turno++;
	}

	public void setPlayerTurn(Player j, boolean turno) {
		j.reset();
		if (turno)
			notificarTurnoJugador(j);
	}

	public int getTurno() {
		// return turno;
		return turno;
	}

	public void addUpdateListener(UpdateListener listener) {
		listeners.add(listener);
	}

	public List<Player> getPlayerList() {
		return playerList;
	}

	public List<Square> getSquareList() {
		return tablero.getSquareList();
	}

	public Square getSquare(int squareNumber) {
		Square casilla = null;
		for (Square c : tablero.getSquareList()) {
			if (squareNumber == c.getNumCasilla())
				casilla = c;
		}
		return casilla;
	}

	public Square getSquare(String nombreCasilla) {
		Square casilla = null;
		for (Square c : tablero.getSquareList()) {
			if (nombreCasilla == c.getSquareName())
				casilla = c;
		}
		return casilla;
	}

	public boolean playerMove(Player j, Square c) {
		if (j != null && c != null && tablero.isSquare(c)) {
			Set<Square> validas = getReachableSquares(j);
			if (validas.contains(c)) {
				Square antigua = j.getUbication();
				if (antigua != null)
					antigua.removePlayer(j);
				j.move(c);
				c.addPlayer(j);
				notificarUbicacionActualizada(j);
				if (c.getPlayerList().size() > 1)
					notifyTwoPlayersHere(j, c);
				return true;
			} else
				return false;
		} else
			return false;
	}

	private void notificarUbicacionActualizada(Player j) {
		for (UpdateListener listener : listeners) {
			listener.ubicacionActualizada(j);
		}
	}

	private void notifyTwoPlayersHere(Player j, Square c) {
		for (UpdateListener listener : listeners) {
			listener.twoPlayersHere(j, c);
		}
	}

	private void notificarPartidaEmpezada() {
		for (UpdateListener listener : listeners) {
			listener.partidaEmpezada();
		}
	}

	private void notificarNuevoJugador(Player j) {
		for (UpdateListener listener : listeners) {
			listener.newPlayer(j);
		}
	}

	private void notificarTurnoJugador(Player j) {
		for (UpdateListener listener : listeners) {
			listener.playerTurn(j);
		}
	}

	public Square getPlayerLocation(Player j) {
		return j.getUbication();
	}

	public void addPlayer(String nombre, Colors color) {
		Player player = new Player(nombre, color);
		playerList.add(player);
		notificarNuevoJugador(player);
	}

	public Player getPlayer(int numero) {
		Player player = null;
		for (Player j : playerList)
			if (numero == j.getNumero())
				player = j;
		return player;
	}

	public void writeDown(Player j, int id, String annotation) {
		Set<Sospechoso> set = j.getHojaSospechosos();
		for (Sospechoso s : set) {
			if (s.getId() == id)
				s.setAnnotation(annotation);
		}
	}

	// TODO: implementar esto mediante hash Map
	public void buildAbadiaMap() {
		tablero.addSquare(new Square(1, "Capilla"));
		tablero.addSquare(new Square(2, "Confesionario1"));
		tablero.addSquare(new Square(3, "Cripta"));
		tablero.addSquare(new Square(4, "Patio"));
		tablero.addSquare(new Square(5, "Confesionario2"));
		tablero.addSquare(new Square(6, "Claustro1"));
		tablero.addSquare(new Square(7, "Claustro2"));
		tablero.addSquare(new Square(8, "Claustro3"));
		tablero.addSquare(new Square(9, "Hall1"));
		tablero.addSquare(new Square(10, "Celda1"));
		tablero.addSquare(new Square(11, "Scriptorium"));
		tablero.addSquare(new Square(12, "Biblioteca"));
		tablero.addSquare(new Square(13, "Hall2"));
		tablero.addSquare(new Square(14, "Celda2"));
		tablero.addSquare(new Square(15, "Claustro4"));
		tablero.addSquare(new Square(16, "Hall3"));
		tablero.addSquare(new Square(17, "Celda3"));
		tablero.addSquare(new Square(18, "Sala capitular"));
		tablero.addSquare(new Square(19, "Hall4"));
		tablero.addSquare(new Square(20, "Celda4"));
		tablero.addSquare(new Square(21, "Hall5"));
		tablero.addSquare(new Square(22, "Celda5"));
		tablero.addSquare(new Square(23, "Locutorio"));
		tablero.addSquare(new Square(24, "Hall6"));
		tablero.addSquare(new Square(25, "Celda6"));
		tablero.connectSquare(getSquare(1), getSquare(2));
		tablero.connectSquare(getSquare(1), getSquare(3));
		tablero.connectSquare(getSquare(1), getSquare(4));
		tablero.connectSquare(getSquare(4), getSquare(5));
		tablero.connectSquare(getSquare(4), getSquare(6));
		tablero.connectSquare(getSquare(6), getSquare(7));
		tablero.connectSquare(getSquare(6), getSquare(8));
		tablero.connectSquare(getSquare(7), getSquare(15));
		tablero.connectSquare(getSquare(7), getSquare(21));
		tablero.connectSquare(getSquare(7), getSquare(23));
		tablero.connectSquare(getSquare(7), getSquare(24));
		tablero.connectSquare(getSquare(8), getSquare(9));
		tablero.connectSquare(getSquare(8), getSquare(11));
		tablero.connectSquare(getSquare(8), getSquare(13));
		tablero.connectSquare(getSquare(8), getSquare(15));
		tablero.connectSquare(getSquare(9), getSquare(10));
		tablero.connectSquare(getSquare(11), getSquare(12));
		tablero.connectSquare(getSquare(13), getSquare(14));
		tablero.connectSquare(getSquare(15), getSquare(16));
		tablero.connectSquare(getSquare(15), getSquare(18));
		tablero.connectSquare(getSquare(15), getSquare(19));
		tablero.connectSquare(getSquare(16), getSquare(17));
		tablero.connectSquare(getSquare(19), getSquare(20));
		tablero.connectSquare(getSquare(21), getSquare(22));
		tablero.connectSquare(getSquare(24), getSquare(25));
	}

	public void giveDeskCardToPlayer(String p, Player j) {
		switch(p)
		{
		case "BIBLIOTECA": notifyViewBiblioCard(j);
			break ;
		case "SCRIPTORIUM": notifyViewScriptoCard(j);
			break;
		case "CRIPTA": notifyViewCriptaCard(j);
			break;
		}
		
	}

	private void notifyViewCriptaCard(Player j) {
		for (UpdateListener listener : listeners) {
			listener.deskCardEventCripta(j);
		}
	}

	private void notifyViewScriptoCard(Player j) {
		for (UpdateListener listener : listeners) {
			listener.deskCardEventScriptorium(j);
		}
	}

	private void notifyViewBiblioCard(Player j) {
		for (UpdateListener listener : listeners) {
			listener.deskCardEventLibrary(j);
		}
	}

	public BibliotecaModel getLastLibrary() {
		// TODO: barajear las cartas despues de quitar
		return biblioteca.getLibraryCards().get(biblioteca.getLibraryCards().size()-1);
	}

	public ScriptoriumModel getLastScriptoriumCard() {
		// TODO: barajear las cartas despues de quitar
		return scriptorium.getScriptoriumCards().get(scriptorium.getScriptoriumCards().size()-1);
	}

	public String getLastCripta() {
		// TODO Auto-generated method stub
		return cripta.getCriptaCards().get(cripta.getCriptaCards().size()-1);
	}

}
